#ifndef __VM_SPACE__
#define __VM_SPACE__

#include "stdafx.h"
#include <map>
#include "vmface.h"
#include "pgtx64.h"

namespace xm
{
	typedef	uint32_t	paga_t;

	// u can alloc 16G(max) virtual memory
	static const uint64_t MAX_AREA_SIZE = 0x400000000LL;

	struct vmarea_t
	{
		vxvma_t				base;
		vxvma_t				size;
		paga_t				paga;
		iptr<IKernelObject>	vmso;
		vmarea_t() : base(0), size(0), paga(0)
		{
		}
		uint32_t get_def_paga( vxvma_t vma );
	};

	typedef std::map<vxvma_t,vmarea_t>	vmamap_t;
	typedef vmamap_t::iterator			vmamapit;

	class space64_t
	{
	protected:

		vmamap_t	_regions;
		
		vxvma_t		_max_address;

	public:

		space64_t() : _max_address(0)
		{}

		vxvma_t top_address() 
		{
			return _max_address;
		}

		long		init( vxvma_t room );

		vmarea_t *	find( vxvma_t addr, size_t state_mask );

		vmamapit	findit( vxvma_t addr, size_t state_mask );
		
		long		release( vmamapit it );

		vmarea_t *	alloc( vxvma_t base, uint64_t bytes, paga_t paga, size_t align );

		long		release( vxvma_t base, uint32_t filter = PGA_FLT_VIEW|PGA_FLT_PRIV );
		long		release( vmarea_t * area, uint32_t filter = PGA_FLT_VIEW|PGA_FLT_PRIV );

		vmarea_t *	query( vxvma_t addr );

		static bool is_valid_region( vmarea_t * p )
		{
			if( p && p->paga ) return true;
			return false;
		}

	protected:

		bool ALLOCABLE( uint64_t addr, size_t align )
		{
			return (addr & (align-1) ) == 0;
		}

		vmamapit allocit( uint64_t base, uint64_t bytes, uint32_t paga, size_t align );

		vmamapit split( vmamapit fit, uint64_t want, bool top, bool gap = false );

		vmamapit find_free_region( uint64_t addr, uint64_t bytes, bool top, size_t align );

		vmamapit get_free_region_bottom( uint64_t bytes, size_t align );

		vmamapit get_free_region_top( uint64_t bytes, size_t align );

		vmamapit get_region_spec( uint64_t addr );

		vmarea_t * get_region_spec_ptr( uint64_t addr );

		vmamapit get_free_region_spec( uint64_t addr, uint64_t bytes );
	};

	struct vmr_auto_free
	{
		space64_t* _space;
		vmarea_t * _region;
		vmr_auto_free( space64_t * space, vmarea_t * region )
			: _space(space), _region(region)
		{
		}
		~vmr_auto_free()
		{
			if( _space && _region )
				_space->release( _region->base );
		}
		void detach()
		{
			_space = 0;
			_region = 0;
		}
	};


};

#endif